package ade.prime.activity;

import ade.prime.activity.adapter.ListLessonAdapter;
import ade.prime.activity.adapter.ListLessonAdapter.ViewType;
import ade.prime.activity.adapter.ResourceFetcher;
import ade.prime.model.AdeWebOptions;
import ade.prime.model.Lesson;
import ade.prime.model.LessonHistory;
import ade.prime.model.LessonType;
import ade.prime.model.Lessons;
import ade.prime.model.Schedule;
import ade.prime.model.ScheduleState;
import ade.prime.service.AdeWebServiceListener;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.content.ComponentName;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.app.NavUtils;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.ArrayAdapter;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.ListAdapter;
import android.widget.TextView;
import android.widget.Toast;

import com.actionbarsherlock.app.SherlockFragment;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;

/**
 * @author		Schnell Michaël
 * @version		1.1
 */
public class ViewSchedule extends AdeWebFragmentActivity
implements BundleExtraNames, DialogInterface.OnClickListener, 
OnCancelListener, OnClickListener {

	@SuppressWarnings("hiding")
	protected static final String TAG = ViewSchedule.class.getSimpleName() ;

	protected static final int DEFAULT_VIEW_TYPE = ViewType.DAILY.ordinal() ;

	private static final int REFRESH_ID = 1 ;
	private static final int ADE_COLOR = 2 ;

	protected Schedule displayedSchedule ;

	protected boolean waitingForUpdate ;

	protected Lessons displayedLessons ;

	protected Lesson flaggedLesson ;

	protected boolean handleDialog ;

	protected ScheduleUpdateDialog updateDialog ;

	protected FragmentAdapter fragmentAdapter ;

	protected ViewPager pager ;

	protected ListLessonAdapter[] adapters ;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState) ;

		Bundle bundle = this.getIntent().getExtras() ;
		this.displayedSchedule = bundle.getParcelable(EXTRA_SCHEDULE) ;
		this.displayedLessons = new Lessons() ;
		this.waitingForUpdate = this.displayedSchedule.getState()
				.equals(ScheduleState.WAITING_FOR_UPDATE) ;
			// bundle.getBoolean(EXTRA_SCHEDULE_UPDATE_SCHEDULED) ;
		this.handleDialog = false ;
		ViewType[] viewTypes = ViewType.values() ;
		this.adapters = new ListLessonAdapter[viewTypes.length] ;
		for(ViewType type : viewTypes) {
			this.adapters[type.ordinal()] = ListLessonAdapter.getInstance(
					this, this.displayedLessons, type) ;
		}// for

		//Fixe le thème de la page
		this.setTheme(AdeWebOptions.getThemeAde(this)) ;
		//Fixe le titre de la page
		this.setTitle(this.displayedSchedule.getName()) ;
		//Fixe le sous-titre de la page
		this.getSupportActionBar().setSubtitle(R.string.ViewSchedule_subtitle) ;
		//Ajoute une petite flèche à gauche du logo dans la barre d'action
		this.getSupportActionBar().setDisplayHomeAsUpEnabled(true);

		this.initialize() ;
	}// onCreate(Bundle)

	/**
	 * {@inheritDoc}
	 * @see android.app.Activity#onStart()
	 */
	@Override
	protected void onStart() {
		super.onStart() ;
		this.handleDialog = true ;

		ScheduleState currentState = this.displayedSchedule.getState() ;
		if (currentState.equals(ScheduleState.UPDATING)) {
			this.displayUpdateDialog(currentState) ;
		}// if
	}// onStart()

	/**
	 * {@inheritDoc}
	 * @see com.actionbarsherlock.app.SherlockActivity#onStop()
	 */
	@Override
	protected void onStop() {
		super.onStop() ;
		this.handleDialog = false ;
	}// onStop()

	/**
	 * {@inheritDoc}
	 * @see android.support.v4.app.FragmentActivity#onBackPressed()
	 */
	@Override
	public void onBackPressed() {
		super.onBackPressed() ;
		this.updateDialog = null ;
	}// onBackPressed()

	@Override 
	public boolean onCreateOptionsMenu(Menu menu) {
		Intent userManualIntent = new Intent(this, UserManual.class) ;

		int iconId = R.drawable.ic_action_refresh_holo_dark ;
		if (this.waitingForUpdate) {
			iconId = R.drawable.cancel ;
		}// if
		menu.add(Menu.NONE, REFRESH_ID, Menu.NONE, R.string.ActionBar_refresh)
		.setIcon(iconId)
		.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT) ;

		menu.add(Menu.NONE, Menu.NONE, Menu.NONE, R.string.ActionBar_help)
		.setIcon(R.drawable.ic_action_help_holo_light)
		.setIntent(userManualIntent)
		.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) ;

		menu.add(Menu.NONE, ADE_COLOR, Menu.NONE, R.string.Preferences_color_ADE_title)
		.setIcon(R.drawable.icon)
		.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER) ;

		return super.onCreateOptionsMenu(menu) ;
	}// onCreateOptionsMenu(Menu)

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch(item.getItemId()) {
		case android.R.id.home:
            NavUtils.navigateUpFromSameTask(this);
			return true ;

		case ADE_COLOR:
			SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this) ;
			Editor edit = prefs.edit() ;
			String adeColorKey = ResourceFetcher.getString(this, R.string.Preferences_color_ADE) ;
			boolean newUserColor = !prefs.getBoolean(adeColorKey, false) ;
			edit.putBoolean(adeColorKey, newUserColor) ;
			edit.commit() ;
			for(ListLessonAdapter adapter : this.adapters) {
				adapter.setUseColor(newUserColor) ;
				adapter.notifyDataSetChanged() ;
			}// for
			return true ;

		case REFRESH_ID:
			try {
				if (this.waitingForUpdate) {
					this.serviceApi.cancelScheduleUpdate(this.displayedSchedule) ;
				} else if (AdeWebOptions.networkIsAvailable(this)) {
					this.serviceApi.updateSchedule(this.displayedSchedule) ;
				} else {// No network is available, no need to try updating
					Toast.makeText(this, R.string.Sync_no_network, Toast.LENGTH_SHORT)
						.show() ;
				}// if
			} catch (RemoteException re) {
				// TODO: handle exception
				Log.d(TAG, "Failed to update/cancel update for schedule: "  //$NON-NLS-1$
						+ this.displayedSchedule.getName(), re) ;
			}// try	
			return true ;
		}// switch

		return super.onOptionsItemSelected(item) ;
	}// onOptionsItemSelected(MenuItem)

	private void initialize() {
		this.setContentView(R.layout.view_schedule) ;

		this.fragmentAdapter = 
				new FragmentAdapter(this.getSupportFragmentManager()) ;

		this.pager = (ViewPager) this.findViewById(R.id.view_schedule_viewpager) ;
		this.pager.setAdapter(this.fragmentAdapter) ;
		this.pager.setPageMarginDrawable(R.drawable.grey_border_inset_lr);
		this.pager.setPageMargin(getResources()
                 .getDimensionPixelSize(R.dimen.page_margin_width));
		this.pager.setCurrentItem(DEFAULT_VIEW_TYPE) ;
	}// initialize()

	protected void displayUpdateDialog(ScheduleState state) {
		if (this.handleDialog) {
			if (this.updateDialog == null) {
				this.updateDialog = new ScheduleUpdateDialog(
						this, R.string.UpdateDialog_title, state.step) ;
			} else {
				this.updateDialog.updateMaxProgress(state.step) ;
			}// if
		}// if
	}// displayUpdateDialog()

	/**
	 * Handles the cancel action.
	 * 
	 * @param	actualView
	 * 			view of the component who has received a click event
	 */
	@Override
	public void onCancel(DialogInterface dialog) {
		dialog.dismiss() ;
		this.onBackPressed() ;
	}// onCancel(DialogInterface)

	protected void dropUpdateDialog() {
		this.updateDialog.dismiss() ;
		this.updateDialog = null ;
	}// dropUpdateDialog()

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void onClick(View v) {
		try {
			this.serviceApi.updateSchedule(this.displayedSchedule) ;
		} catch (RemoteException re) {
			// TODO: handle exception
			Log.d(TAG, "Failed to update schedule: " //$NON-NLS-1$
					+ this.displayedSchedule.getName(), re) ;
		}// try
	}// onClick(View)

	protected void fetchScheduleLessons() {
		int scheduleId = this.displayedSchedule.getId() ;
		try {
			this.displayedLessons = 
					this.serviceApi.getFutureScheduleLessons(scheduleId) ;
			this.fragmentAdapter.setLessons(this.displayedLessons) ;
			for(ListLessonAdapter adapter : this.adapters) {
				adapter.setLessons(this.displayedLessons, this) ;
			}// for
		} catch (RemoteException re) {
			Log.d(TAG, String.format(
					"Failed to retrieve schedule lessons '%s'", //$NON-NLS-1$ 
					Integer.valueOf(scheduleId)),
					re) ;
		}// try
	}// fetchScheduleLessons()

	/**
	 * {@inheritDoc}
	 * @see ade.prime.activity.AdeWebActivity#getServiceConnection()
	 */
	@Override
	protected ServiceConnection getServiceConnection() {
		return new ServiceConnectionAdapter(this) ;
	}// getServiceConnection()

	/**
	 * {@inheritDoc}
	 * @see ade.prime.activity.AdeWebActivity#getAdeWebServiceListener()
	 */
	@Override
	protected AdeWebServiceListener getAdeWebServiceListener() {
		return new AdeWebServiceListenerAdapter(this) ;
	}// getAdeWebServiceListener()

	private static class UpdateScheduleListener implements OnClickListener {

		protected ViewSchedule viewSchedule ;

		/**
		 * 
		 */
		protected UpdateScheduleListener(ViewSchedule vs) {
			super() ;
			this.viewSchedule = vs ;
		}// UpdateScheduleListener()

		/**
		 * {@inheritDoc}
		 * @see android.view.View.OnClickListener#onClick(android.view.View)
		 */
		@Override
		public void onClick(View v) {
			try {
				if (AdeWebOptions.networkIsAvailable(this.viewSchedule)) {
					this.viewSchedule.serviceApi.updateSchedule(
							this.viewSchedule.displayedSchedule) ;
				} else {// No network is available, no need to try updating
					Toast.makeText(this.viewSchedule, R.string.Sync_no_network, Toast.LENGTH_SHORT)
					.show() ;
				}// if
			} catch (RemoteException e) {
				// TODO Handle exception
				Log.d(TAG, "Failed to update schedule.", e) ; //$NON-NLS-1$
			}// try
		}// onClick(View)

	}// class UpdateScheduleListener

	private class ServiceConnectionAdapter
	extends AdeWebFragmentActivity.ServiceConnectionAdapter {

		//		private final String mTAG = String.format("%s.%s", //$NON-NLS-1$
		//				ViewSchedule.class.getSimpleName(),
		//				ServiceConnectionAdapter.class.getSimpleName()) ;

		protected ServiceConnectionAdapter(AdeWebFragmentActivity activity) {
			super(activity) ;
		}// ServiceConnectionAdapter(AdeWebFragmentActivity)

		/**
		 * {@inheritDoc}
		 * @see ade.prime.activity.AdeWebActivity.ServiceConnectionAdapter#onServiceConnected(android.content.ComponentName, android.os.IBinder)
		 */
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			super.onServiceConnected(name, service) ;

			ViewSchedule vs = (ViewSchedule) this.activity ;
			if (!vs.displayedSchedule.getState().equals(ScheduleState.UPDATING)) {
				vs.fetchScheduleLessons() ;
			}// if
		}// onServiceConnected(ComponentName, IBinder)

	}// class ServiceConnectionAdapter

	private class AdeWebServiceListenerAdapter
	extends AdeWebFragmentActivity.AdeWebServiceListenerAdapter {

		/**
		 * @param activity
		 */
		protected AdeWebServiceListenerAdapter(ViewSchedule activity) {
			super(activity) ;
		}// AdeWebServiceListenerAdapter(ViewSchedule)

		@Override
		public void handleModifiedLessonTypeRunnable(Lesson modifiedLesson) {
			ViewSchedule vs = (ViewSchedule) this.activity ;
			int concernedSchedule = modifiedLesson.getScheduleId() ;
			if (concernedSchedule == vs.displayedSchedule.getId()) {
				long modifiedId = modifiedLesson.getLessonId() ;
				for(Lesson lesson : vs.displayedLessons) {
					if (lesson.getLessonId() == modifiedId) {
						lesson.setType(modifiedLesson.getType()) ;
						break ;
					}// if
				}// for
				for(ListLessonAdapter adapter : vs.adapters) {
					adapter.updateLessonType(modifiedLesson) ;
				}// for
			}// if
		}// handleModifiedLessonType(Lesson)

		//TODO Review function (update dialog is not working properly)
		@Override
		public void handleModifiedScheduleStateRunnable(Schedule modifiedSchedule) {
			ViewSchedule vs = (ViewSchedule) this.activity ;
			if (vs.displayedSchedule.getName().equals(modifiedSchedule.getName())) {
				ScheduleState newState = modifiedSchedule.getState() ;
				vs.displayedSchedule.setState(newState) ;
				boolean updateScheduled = 
						newState.equals(ScheduleState.WAITING_FOR_UPDATE) ;
				if (updateScheduled != vs.waitingForUpdate) {
					vs.waitingForUpdate = updateScheduled ;
					vs.invalidateOptionsMenu() ;
				}// if
				if (newState.equals(ScheduleState.UPDATING)) {
					vs.displayUpdateDialog(newState) ;
				} else if (newState.equals(ScheduleState.UPDATED)) {
//					Toast.makeText(vs, 
//							R.string.ViewSchedule_update_success, 
//							Toast.LENGTH_SHORT)
//						.show() ;
					
					vs.fetchScheduleLessons() ;
					vs.updateDialog = null ;
				} else if (newState.equals(ScheduleState.NOT_UPDATED)) {
					vs.updateDialog.dismiss() ;
					vs.updateDialog = null ;
					
					Toast.makeText(vs, 
							R.string.ViewSchedule_update_fail, 
							Toast.LENGTH_SHORT)
						.show() ;
				}// if
			}// if
		}// handleModifiedScheduleStateRunnable(Schedule)
		
		/**
		 * {@inheritDoc}
		 */
		@Override
		protected void handleRemovedLessonHistoryRunnable(long lessonId) {
			ViewSchedule vs = (ViewSchedule) this.activity ;
			for(Lesson lesson : vs.displayedLessons) {
				if (lesson.getLessonId() == lessonId) {
//					Log.d(TAG, "Lesson found and history removed !") ;
					lesson.setHistory((LessonHistory) null) ;
					break ;
				}// if
			}// for
			for(ListLessonAdapter adapter : vs.adapters) {
				adapter.removeLessonHistory(lessonId) ;
			}// for
		}// handleRemovedLessonHistoryRunnable(long)

	}// class AdeWebServiceListenerAdapter

	/**
	 * {@inheritDoc}
	 * @see DialogInterface.OnClickListener#onClick(DialogInterface, int)
	 */
	@Override
	public void onClick(DialogInterface dialog, int which) {
		//		Log.d(TAG, "Click on item " + which) ; //$NON-NLS-1$

		LessonType[] lessonTypes = LessonType.values() ;
		LessonType newType = lessonTypes[which] ;
		if (!newType.equals(this.flaggedLesson.getType())) {
			try {
				this.serviceApi.setLessonType(this.flaggedLesson, newType) ;
			} catch (RemoteException re) {
				Log.d(TAG, String.format("Failed to mark lesson '%s' (%s)",  //$NON-NLS-1$
						this.flaggedLesson.getTitle(), 
						Long.valueOf(this.flaggedLesson.getLessonId())), re) ;
			}// try
		}// if

		dialog.dismiss() ;
	}// onClick(DialogInterface, int)

	public static class FragmentAdapter extends FragmentStatePagerAdapter {

		protected Lessons lessons ;

		public FragmentAdapter(FragmentManager fm) {
			super(fm) ;
		}// FragmentAdapter(FragmentManager)

		/**
		 * @param lessons the lessons to set
		 */
		public void setLessons(Lessons lessons) {
			this.lessons = lessons ;
			this.notifyDataSetChanged() ;
			//			Log.d("FragmentAdapter", "[setLessons]") ;
		}// setLessons(Lessons)

		@Override
		public int getCount() {
			return ViewType.values().length ;
		}// getCount()

		@Override
		public Fragment getItem(int position) {
			//			Log.d("FragmentAdapter", "[getItem] - position: " + position) ;
			//			Log.d("FragmentAdapter", "[getItem] -  lessons: " + this.lessons.size()) ;
			return ViewScheduleFramgent.newInstance(
					ViewType.values()[position], this.lessons) ;
		}// getItem(int)
	}// class FragmentAdapter

	public static class ViewScheduleFramgent extends SherlockFragment
	implements OnChildClickListener, OnItemLongClickListener {

		private ViewType viewType ;

		private ListLessonAdapter adapter ;

		private Lessons lessons ;

		/**
		 * Create a new instance.
		 */
		public static ViewScheduleFramgent newInstance(ViewType viewType,
				Lessons lessons) {
			//			Log.d("ViewScheduleFramgent", "ViewType: " + viewType
			//					+ " Lessons: " + lessons.size()) ;
			ViewScheduleFramgent fragment = new ViewScheduleFramgent() ;

			// Supply viewType ordinal as an argument.
			Bundle args = new Bundle() ;
			args.putInt(EXTRA_SCHEDULE_VIEW_TYPE, viewType.ordinal()) ;
			args.putParcelable(EXTRA_SCHEDULE_LESSONS, lessons) ;
			fragment.setArguments(args) ;

			return fragment;
		}// newInstance(ViewType)

		/**
		 * When creating, retrieve this instance's number from its arguments.
		 */
		@Override
		public void onCreate(Bundle savedInstanceState) {
			super.onCreate(savedInstanceState) ;
			Bundle args = this.getArguments() ;
			if (args != null) {
				int viewTypeOrdinal = args.getInt(EXTRA_SCHEDULE_VIEW_TYPE) ;
				this.viewType = ViewType.values()[viewTypeOrdinal] ;
				this.lessons = args.getParcelable(EXTRA_SCHEDULE_LESSONS) ;
			}// if
		}// onCreate(Bundle)

		/**
		 * The Fragment's UI is just a simple text view showing its
		 * instance id.
		 */
		@Override
		public View onCreateView(LayoutInflater inflater, ViewGroup container,
				Bundle savedInstanceState) {        	
			View v = inflater.inflate(
					R.layout.lesson_list, container, false) ;

			ExpandableListView list = 
					(ExpandableListView) v.findViewById(R.id.lesson_list) ; 
			list.setEmptyView(v.findViewById(R.id.empty)) ;
			list.setBackgroundColor(AdeWebOptions.getBackgroundColor(AdeWebOptions.getTheme(this.getActivity())));
			list.setCacheColorHint(0);

			return v ;
		}// onCreateView(LayoutInflater, ViewGroup, Bundle)

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void onConfigurationChanged(Configuration newConfig) {
			super.onConfigurationChanged(newConfig) ;
			View view = this.getView() ;
			ExpandableListView list = 
					(ExpandableListView) view.findViewById(R.id.lesson_list) ;
			expandAllItems(this.adapter, list) ;
		}// onConfigurationChanged(Configuration)

		//		/**
		//		 * {@inheritDoc}
		//		 */
		//		@Override
		//		public void onConfigurationChanged(Configuration newConfig) {
		//			super.onConfigurationChanged(newConfig) ;
		//			int nFragments = this.fragmentAdapter.getCount() ;
		//			for(int f=0; f < nFragments; ++f) {
		//				ViewScheduleFramgent vsf = (ViewScheduleFramgent) 
		//						this.fragmentAdapter.getItem(f) ;
		//				
		//				expandAllItems(this.adapters[f], 
		//						(ExpandableListView) vsf.getView().findViewById(R.id.lesson_list)) ;
		//			}// for
		//		}// onConfigurationChanged(Configuration)

		protected static void expandAllItems(ListLessonAdapter adapter, ExpandableListView listView) {
			int nGroups = adapter.getGroupCount() ;
			for(int pos=0; pos<nGroups; ++pos) {
				listView.expandGroup(pos) ;
			}// for
		}// expandAllItems(ListLessonAdapter, ExpandableListView)

		@Override
		public void onActivityCreated(Bundle savedInstanceState) {
			super.onActivityCreated(savedInstanceState) ;
			View view = this.getView() ;
			ExpandableListView list = 
					(ExpandableListView) view.findViewById(R.id.lesson_list) ;
			ViewSchedule vs = (ViewSchedule) this.getActivity() ;

			this.adapter = vs.adapters[this.viewType.ordinal()] ;
			list.setAdapter(this.adapter) ;
			expandAllItems(this.adapter, list) ;

			TextView updateText = (TextView) view.findViewById(R.id.update_schedule) ;
			updateText.setOnClickListener(new UpdateScheduleListener(vs)) ;

			this.registerForContextMenu(list) ;
			list.setOnChildClickListener(this) ;
			list.setOnItemLongClickListener(this) ;
		}// onActivityCreated(Bundle)

		/**
		 * {@inheritDoc}
		 */
		@Override
		public boolean onChildClick(ExpandableListView parent, View v, 
				int groupPosition, int childPosition, long id) {
			Intent viewLesson = new Intent(this.getActivity(), LessonDetails.class) ;
			viewLesson.putExtra(EXTRA_SCHEDULE_LESSONS, this.lessons) ;
			viewLesson.putExtra(EXTRA_DISPLAYED_LESSON, id) ;
			this.startActivity(viewLesson) ;
			return true ;
		}// onChildClick(ExpandableListView, View, int, int, long)

		/**
		 * {@inheritDoc}
		 */
		@Override
		public boolean onItemLongClick(AdapterView<?> parent, View view, int position, 
				long id) {
			if ((id & 0x8000000000000000L) != 0) {
				int childId = (int) (id & 0xffffffffL) ;
				//				int groupId = ((int) (id >>> 32)) & 0x7fffffff ;
				//				Log.d(TAG, String.format("Position: %s, id: %016X, group: %08X, child: %08X",
				//						position, id, groupId, childId)) ;
				ViewSchedule vs = (ViewSchedule) this.getActivity() ;
				vs.flaggedLesson = vs.displayedLessons.get(childId) ;
				LessonType currentType = vs.flaggedLesson.getType() ;

				AlertDialog.Builder builder = new Builder(vs) ;
				ListAdapter dialogAdapter = new ArrayAdapter<LessonType>(
						vs, 
						android.R.layout.select_dialog_singlechoice, 
						LessonType.values()) ;
				builder.setSingleChoiceItems(dialogAdapter, currentType.ordinal(), vs) ;
				builder.setTitle(R.string.ViewSchedule_mark_dialog_title) ;
				builder.setIcon(RESULT_OK) ;
				builder.create().show() ;

				return true ;
			}// if
			return false ;
		}// onItemLongClick(AdapterView<?>, View, int, long)

	}// class ViewScheduleFramgent

}// class ViewSchedule